package assign4;

/**
 * Based on pass the baton from p177 of text
 * Allows multiple readers to read simultaneously
 * prevents starvation of readers or writers
 */
public class DatabaseLock
{	
	private Sem e = new Sem(1); // controls entry to critical sections
	private Sem r = new Sem(0); // the signal that it is safe to read
	private Sem w = new Sem(0); // the signal that it is safe to write
	
	private int nr = 0; // the number of current readers
	private int nw = 0; // the number of current writers
	
	int dr = 0; // the number of delayed readers
	int dw = 0; // the number of delayed writers
	
	/** Call before a write operation
	 * 
	 * Delays until there are no writers or, delayed writers
	 */
	public void startRead()
	{
		// <await(nw == 0 && dw == 0) dr++;>
		e.P();
		if(nw > 0 || dw > 0) 
		{ 
			dr++;
			e.V();
			r.P();
		}
		nr++;
		if(dr > 0) 
		{
			dr--;
			r.V();
		}
		else 
		{
			e.V();
		}
	}
	
	/** Call at the end of a write operation
	 * 
	 * Decrements the number of readers
	 * if(there are no readers and there are writers waiting)
	 * 		signal a writer
	 * else
	 * 		release the mutex
	 */
	public void endRead()
	{
		// <nr-->
		e.P();
		nr--;
		if(nr == 0 && dw > 0) 
		{
			dw--;
			w.V();
		}
		else 
		{
			e.V();
		}
	}
	
	/** Call before starting a write operation
	 *
	 * 	Delays until, there are no readers, writers,
	 *     and no delayed readers
	 */
	public void startWrite()
	{
		// <await(nr == 0 && nw == 0 && dr == 0) dw++;>
		e.P();
		if(nr > 0 || nw > 0 || dr > 0) 
		{
			dw++;
			e.V();
			w.P();
		}
		nw++;
		e.V();
	}
	
	/** Call after a write operation completes
	 * 
	 * Decrements the number of writers.
	 * if (there are any delayed readers)
	 * 		signal all readers to read
	 * else if(there are delayed writers)
	 * 		signal a writer to write
	 * else 
	 * 		release the mutex
	 */
	public void endWrite()
	{
		// <nw-->
		e.P();
		nw--;
	    if(dr > 0) 
	    { 
			dr--; 
			r.V(); 
		}
	    else if(dw > 0)
	    {
	    	dw--;
	    	w.V();
	    }
		else {
			e.V();
		}
	}
}
